﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using BicycleTrafficSimulator.DataModel;
using BicycleTrafficSimulator.Presentation;
using BicycleTrafficSimulator.Business;
using Microsoft.Xna.Framework.Content;

namespace BicycleTrafficSimulator
{
    class Game : DrawableGameComponent
    {
        public ContentManager gameContent;

        protected const String WINDOW_TITLE = "Bicycle Traffic Simulator";
        protected const Boolean FULL_SCREEN = false;
        protected bool inIntersection = false;
        protected bool once = true;
        protected bool gamePaused = false;
        protected int timer = 100;

        protected Vector3 campos;
        protected Vector3 camup;

        protected SpriteBatch spriteBatch;
        protected Matrix viewMatrix;
        protected Matrix projectionMatrix;

        //the rotation value of the imported model
        protected float previousModelRotation = 300.0f;
        protected String[] directions = { "N", "E", "S", "V" };
        protected int dirIndex = 0;
        protected int signPosition = 1;

        protected Vector3 lightDirection = new Vector3(3, -2, 5);
        protected Quaternion cameraRotation = Quaternion.Identity;

        public Quaternion CameraRotation
        {
            get { return cameraRotation; }
            set { cameraRotation = value; }
        }
        protected float gameSpeed = 1.0f;

        public float GameSpeed
        {
            get { return gameSpeed; }
            set { gameSpeed = value; }
        }
        private GameTime gameTimeGlobal;

        public GameTime GameTimeGlobal
        {
            get { return gameTimeGlobal; }
            set { gameTimeGlobal = value; }
        }

        protected float speedUpDownValue = 0;

        public float SpeedUpDownValue
        {
            get { return speedUpDownValue; }
            set { speedUpDownValue = value; }
        }
        protected int errorCode;
        private Score score;

        public Score Score
        {
            get { return score; }
            set { score = value; }
        }

        protected Vector3 addVectorUGlobal;
        //models
        protected Car car;
        protected Tree tree;
        protected List<Tree> treeList = new List<Tree>();
        protected City city;
        protected Bike bike;

        internal Bike Bike
        {
            get { return bike; }
            set { bike = value; }
        }

        protected TrafficSign roadSign;
        protected List<TrafficSign> roadSignList = new List<TrafficSign>();
        protected List<String> signList = new List<String>(new string[]
	{
        "priorityRoadSign",    
        "prioritySign",    
        "stop",
        "yieldSign"
	});

        protected SkyBox skyBox;
        protected CollisionController collisionController;
        protected SignDetectingController signDetectingController;

        protected FeedbackScreen stopFeedbackScreen, priorityFeedbackScreen, priorityRoadFeedbackScreen, yieldFeedbackScreen, crashFeedbackScreen, successFeedbackScreen;
        protected KeyboardHandler inputHandler;
        protected KinectController kinectController;
        public Scene scene;

        bool closed = false;

        private EditorControls ec;

        public bool GameClosed
        {
            get { return closed; }
            set { closed = value; }
        }

        public Game(Scene _scene)
            : base(_scene)
        {
            scene = _scene;

        }

        public override void Initialize()
        {
            gameContent = new ContentManager(scene.Content.ServiceProvider, scene.Content.RootDirectory);

            this.bike = new Bike();
            this.skyBox = new SkyBox();

            lightDirection.Normalize();

            this.inputHandler = new KeyboardHandler(this);
            this.collisionController = new CollisionController(city);
            this.signDetectingController = new SignDetectingController();
            this.score = new Score();

            kinectController = new KinectController(this);
            this.kinectController.Initialize();

            if (!kinectController.KinectOn)
            {
                this.ec = new EditorControls(scene);
                ec.Initialize();
            }

            base.Initialize();
        }

        private string GetSignName()
        {
            //Random randNum = new Random();
            //int signPosition = randNum.Next(signList.Count);//Returns a nonnegative random number less than the specified maximum (firstNames.Count).
            if (signPosition == 3) signPosition = 0;
            else signPosition++;
            return signList[signPosition];
        }

        protected override void LoadContent()
        {

            string signName;

            spriteBatch = new SpriteBatch(GraphicsDevice);

            stopFeedbackScreen = new FeedbackScreen(scene, spriteBatch, gameContent.Load<SpriteFont>("ScreenFont"), gameContent.Load<Texture2D>("stopFeedbackScreen"), score);
            scene.Components.Add(stopFeedbackScreen);
            stopFeedbackScreen.Hide();

            priorityFeedbackScreen = new FeedbackScreen(scene, spriteBatch, gameContent.Load<SpriteFont>("ScreenFont"), gameContent.Load<Texture2D>("priorityFeedbackScreen"), score);
            scene.Components.Add(priorityFeedbackScreen);
            priorityFeedbackScreen.Hide();

            priorityRoadFeedbackScreen = new FeedbackScreen(scene, spriteBatch, gameContent.Load<SpriteFont>("ScreenFont"), gameContent.Load<Texture2D>("priorityRoadFeedbackScreen"), score);
            scene.Components.Add(priorityRoadFeedbackScreen);
            priorityRoadFeedbackScreen.Hide();

            yieldFeedbackScreen = new FeedbackScreen(scene, spriteBatch, gameContent.Load<SpriteFont>("ScreenFont"), gameContent.Load<Texture2D>("yieldFeedbackScreen"), score);
            scene.Components.Add(yieldFeedbackScreen);
            yieldFeedbackScreen.Hide();

            successFeedbackScreen = new FeedbackScreen(scene, spriteBatch, gameContent.Load<SpriteFont>("ScreenFont"), gameContent.Load<Texture2D>("successFeedbackScreen"), score);
            scene.Components.Add(successFeedbackScreen);
            successFeedbackScreen.Hide();

            crashFeedbackScreen = new FeedbackScreen(scene, spriteBatch, gameContent.Load<SpriteFont>("ScreenFont"), gameContent.Load<Texture2D>("crashFeedbackScreen"), score);
            scene.Components.Add(crashFeedbackScreen);
            crashFeedbackScreen.Hide();

            bike.LoadContent(gameContent, "bike");
            skyBox.LoadContent(gameContent, "skybox2");
            car.LoadContent(gameContent, "car1");

            if (collisionController.SignBoundingBoxes.Length != 0)
            {
                for (int i = 0; i < collisionController.SignBoundingBoxes.Length; i++)
                {
                    roadSign = new TrafficSign(collisionController.SignCoordinates[i].position, collisionController.SignCoordinates[i].rotation);
                    signName = GetSignName();
                    collisionController.SignCoordinates[i].type = signName;
                    roadSign.LoadContent(gameContent, signName);
                    roadSignList.Add(roadSign);
                }
            }

            for (int i = 0; i < collisionController.TreeBoundingBoxes.Length; i++)
            {
                tree = new Tree(collisionController.TreePositions[i]);
                tree.LoadContent(gameContent, "tree");
                treeList.Add(tree);
            }
            SetUpCamera();
        }

        private void SetUpCamera()
        {
            viewMatrix = Matrix.CreateLookAt(campos, bike.Position + new Vector3(0, 0.18f, 0f), camup);
            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 0.2f, 10000.0f);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            kinectController.Unload();
            gameContent.Unload();
        }

        public override void Update(GameTime gameTime)
        {

            addVectorUGlobal = Vector3.Transform(new Vector3(0, 0, -1), cameraRotation);
            //speedUpDownValue = 0.0f;

            if (!gamePaused)
            {
                timer = 100;
                gameTimeGlobal = gameTime;
                if (kinectController.KinectOn)
                    kinectController.Update(gameTime);
                inputHandler.Update(gameTime);
                car.Update(gameTime);
            }
            else timer--;

            BoundingSphere bikeSphere = new BoundingSphere(bike.Position, 0.08f);
            BoundingSphere carSphere = new BoundingSphere(car.Position, 0.5f);
            String collision = collisionController.CheckCollision(bikeSphere);
            float moveSpeed = gameTime.ElapsedGameTime.Milliseconds / 450.0f * gameSpeed;

            if (collision == "Building")
            {
                Vector3 addVectorU = Vector3.Transform(new Vector3(0, 0, 1), cameraRotation);

                if ((moveSpeed + speedUpDownValue) > 0)
                    bike.Position += (addVectorU * (moveSpeed + speedUpDownValue));
            }
            else if (collision == "None")
            {
                if (signDetectingController.IsCorrect == false)
                {
                    switch (errorCode)
                    {
                        case 1:
                            stopFeedbackScreen.Show();
                            gamePaused = true;
                            score.decreaseValue();
                            break;
                        case 2:
                            yieldFeedbackScreen.Show();
                            gamePaused = true;
                            score.decreaseValue();
                            break;
                        case 3:
                            priorityFeedbackScreen.Show();
                            gamePaused = true;
                            break;
                        case 4:
                            priorityRoadFeedbackScreen.Show();
                            gamePaused = true;
                            break;
                    }
                }
                if (signDetectingController.IsCorrect == true)
                {
                    //successFeedbackScreen.Show();
                    //gamePaused = true;
                    SoundEffect soundEffect;
                    soundEffect = scene.Content.Load<SoundEffect>("wellDone");
                    soundEffect.Play();
                    score.increaseValue();
                }
                errorCode = 0;
                signDetectingController.resetValues();
            }
            else if (collision != null)
            {
                Console.WriteLine(speedUpDownValue);
                this.errorCode = signDetectingController.DetectSign(collision, speedUpDownValue);
            }



            if (bikeSphere.Intersects(carSphere))
            {
                Vector3 addVectorU = Vector3.Transform(new Vector3(1, 0, 0), cameraRotation);

                if ((moveSpeed + speedUpDownValue) > 0)
                {
                    bike.Position += (addVectorU * (moveSpeed + speedUpDownValue));
                }
                if (once)
                {
                    crashFeedbackScreen.Show();
                    once = false;
                    gamePaused = true;
                    score.decreaseValue();
                }
            }
            if (timer == 0)
            {
                stopFeedbackScreen.Hide();
                yieldFeedbackScreen.Hide();
                priorityFeedbackScreen.Hide();
                priorityRoadFeedbackScreen.Hide();
                crashFeedbackScreen.Hide();
                successFeedbackScreen.Hide();
                gamePaused = false;
            }

            UpdateCamera();
            base.Update(gameTime);
        }


        private void UpdateCamera()
        {
            campos = new Vector3(0f, 0.20f, 0.15f);
            campos = Vector3.Transform(campos, Matrix.CreateFromQuaternion(cameraRotation));
            campos += bike.Position;

            camup = new Vector3(0, 1, 0);
            camup = Vector3.Transform(camup, Matrix.CreateFromQuaternion(cameraRotation));

            viewMatrix = Matrix.CreateLookAt(campos, bike.Position + new Vector3(0, 0.18f, 0f), camup);
            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 0.2f, 10000.0f);
        }

        public override void Draw(GameTime gameTime)
        {
            skyBox.Draw(viewMatrix, projectionMatrix, bike.Position, GraphicsDevice);
            city.Draw(viewMatrix, projectionMatrix, lightDirection, GraphicsDevice, campos, camup);
            bike.Draw(viewMatrix, projectionMatrix);
            car.Draw(viewMatrix, projectionMatrix);
            foreach (TrafficSign roadSign in roadSignList)
            {
                roadSign.Draw(viewMatrix, projectionMatrix);
            }
            if (treeList != null)
            {
                foreach (Tree treeObject in treeList)
                {
                    treeObject.Draw(viewMatrix, projectionMatrix);
                }
            }

            base.Draw(gameTime);
        }

        protected virtual void OnExiting(Object sender, EventArgs args)
        {
            GameClosed = true;
        }

    }
}
